home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / CW GUSI 1.6.4 / src / GUSIFile.cp < prev    next >
Text File  |  1995-10-25  |  28KB  |  1,382 lines

  1. /*********************************************************************
  2. Project    :    GUSI                -    Grand Unified Socket Interface
  3. File        :    GUSIFile.cp        -    Implementation of file calls
  4. Author    :    Matthias Neeracher <neeri@iis.ethz.ch>
  5. Language    :    MPW C
  6.  
  7. $Log: GUSIFile.cp,v $
  8. Revision 1.4  1994/12/30  19:56:13  neeri
  9. New dispatching mechanism for file names.
  10. Add chmod().
  11.  
  12. Revision 1.3  1994/08/10  00:29:25  neeri
  13. Sanitized for universal headers.
  14.  
  15. Revision 1.2  1994/05/01  23:37:55  neeri
  16. Added utime().
  17. Fixed stat() permissions for locked files.
  18.  
  19. Revision 1.1  1994/03/08  23:51:24  neeri
  20. Initial revision
  21.  
  22. Revision 0.34  1993/09/11  00:00:00  neeri
  23. Trying to avoid stream.h unless absolutely necessary    
  24.  
  25. Revision 0.33  1993/08/25  00:00:00  neeri
  26. Need 2 include TextUtils since E.T.O. #12
  27.  
  28. Revision 0.32  1993/07/17  00:00:00  neeri
  29. Adapt to BSD 4.3, scandir
  30.  
  31. Revision 0.31  1993/06/27  00:00:00  neeri
  32. f?truncate
  33.  
  34. Revision 0.30  1993/02/23  00:00:00  neeri
  35. fgetfileinfo
  36.  
  37. Revision 0.29  1993/02/01  00:00:00  neeri
  38. Made nlink for directories return something meaningful
  39.  
  40. Revision 0.28  1993/01/15  00:00:00  neeri
  41. rename() has to be more careful about renaming order
  42.  
  43. Revision 0.27  1993/01/15  00:00:00  neeri
  44. choose() should *not* count the string terminator.
  45.  
  46. Revision 0.26  1993/01/03  00:00:00  neeri
  47. Respect configuration
  48.  
  49. Revision 0.25  1993/01/01  00:00:00  neeri
  50. fsetfileinfo
  51.  
  52. Revision 0.24  1992/12/20  00:00:00  neeri
  53. do_putfile now respects default
  54.  
  55. Revision 0.23  1992/12/13  00:00:00  neeri
  56. stat now returns DirID/FileNo, not parID for the ino field
  57.  
  58. Revision 0.22  1992/12/08  00:00:00  neeri
  59. getcwd()
  60.  
  61. Revision 0.21  1992/11/28  00:00:00  neeri
  62. TEXT files are now considered executable
  63.  
  64. Revision 0.20  1992/11/15  00:00:00  neeri
  65. Rename GUSIFSp_P.h to TFileSpec.h (there we go again)
  66.  
  67. Revision 0.19  1992/10/28  00:00:00  neeri
  68. Forgot to change to dirent
  69.  
  70. Revision 0.18  1992/09/15  00:00:00  neeri
  71. Slight error in do_stat()
  72.  
  73. Revision 0.17  1992/09/12  00:00:00  neeri
  74. Rename Paths.h to GUSIFSp_P.h
  75.  
  76. Revision 0.16  1992/09/08  00:00:00  neeri
  77. readlink()
  78.  
  79. Revision 0.15  1992/09/06  00:00:00  neeri
  80. Adapt alias resolution to new libraries
  81.  
  82. Revision 0.14  1992/07/13  00:00:00  neeri
  83. CopyIconFamily
  84.  
  85. Revision 0.13  1992/06/27  00:00:00  neeri
  86. choose()
  87.  
  88. Revision 0.12  1992/06/26  00:00:00  neeri
  89. symlink is starting to work
  90.  
  91. Revision 0.11  1992/06/21  00:00:00  neeri
  92. symlink()
  93.  
  94. Revision 0.10  1992/06/15  00:00:00  neeri
  95. Separated path stuff
  96.  
  97. Revision 0.9  1992/05/21  00:00:00  neeri
  98. Implemented select()
  99.  
  100. Revision 0.8  1992/04/20  00:00:00  neeri
  101. C++ rewrite
  102.  
  103. Revision 0.7  1992/04/05  00:00:00  neeri
  104. lseek
  105.  
  106. Revision 0.6  1992/03/22  00:00:00  neeri
  107. Adapted for GUSI, change chdir stuff
  108.  
  109. Revision 0.5  1992/02/11  00:00:00  neeri
  110. Incorporated bug fix by John Reekie
  111.  
  112. Revision 0.4  1991/12/12  00:00:00  neeri
  113. FSp2RelPath
  114.  
  115. Revision 0.3  1991/12/09  00:00:00  neeri
  116. Radical overhaul
  117.  
  118. Revision 0.2  1991/05/28  00:00:00  neeri
  119. isatty()
  120.  
  121. Revision 0.1  1991/05/28  00:00:00  neeri
  122. Created
  123.  
  124. *********************************************************************/
  125.  
  126. #include "GUSIFile_P.h"
  127. #include "TFileSpec.h"
  128.  
  129. #include <Errors.h>
  130. #include <Resources.h>
  131. #include <Script.h>
  132. #include <Finder.h>
  133. #include <Folders.h>
  134. #include <Devices.h>
  135. #include <Memory.h>
  136. #include <Aliases.h>
  137. #include <string.h>
  138. #include <PLStringFuncs.h>
  139. #include <errno.h>
  140. #ifdef GUSI_FILE_DEBUG
  141. #include <stream.h>
  142. #endif
  143. #include <StdLib.h>
  144. #include <Time.h>
  145. #include <TextUtils.h>
  146. #include <unistd.h>
  147. #include <utime.h>
  148.  
  149. #pragma segment GUSI
  150.  
  151. FileSocketDomain    *        FileSockets;    
  152.  
  153. /********************* FileSocketDomain members *********************/
  154.  
  155. #ifndef GUSI_DISPATCH
  156. Boolean IsDevice(const char * fn)
  157. {
  158.     return     (    
  159.         (fn[0] | 0x20) == 'd'
  160.     && (fn[1] | 0x20) == 'e'
  161.     && (fn[2] | 0x20) == 'v'
  162.     && fn[3] == ':');
  163. }
  164.  
  165. int File_error(OSErr err)
  166. {
  167.     switch (err) {
  168.     case noErr:
  169.         errno = 0;
  170.         
  171.         return 0;
  172.     case bdNamErr:
  173.         return GUSI_error(ENAMETOOLONG);
  174.     case afpObjectTypeErr:
  175.         return GUSI_error(ENOTDIR);
  176.     case fnfErr:
  177.     case dirNFErr:
  178.         return GUSI_error(ENOENT);
  179.     case dupFNErr:
  180.         return GUSI_error(EEXIST);
  181.     case dirFulErr:
  182.     case dskFulErr:
  183.         return GUSI_error(ENOSPC);
  184.     case fBsyErr:
  185.         return GUSI_error(EBUSY);
  186.     case tmfoErr:
  187.         return GUSI_error(ENFILE);
  188.     case fLckdErr:
  189.     case permErr:
  190.     case afpAccessDenied:
  191.         return GUSI_error(EACCES);
  192.     case wPrErr:
  193.     case vLckdErr:
  194.         return GUSI_error(EROFS);
  195.     case badMovErr:
  196.         return GUSI_error(EINVAL);
  197.     case diffVolErr:
  198.         return GUSI_error(EXDEV);
  199.     default:
  200.         return GUSI_error(EINVAL);
  201.     }
  202. }
  203.  
  204. Socket * FileSocketDomain::open(const GUSIFileRef & ref, int oflag)
  205. {
  206.     Socket *    sock;
  207.     
  208.     if (ref.IsDevice())
  209.         if (MPWDomain::open)
  210.             return (Socket *) MPWDomain::open(ref.name, oflag);
  211.         else
  212.             return (Socket *) GUSI_error_nil(EINVAL);
  213.     else {
  214.         Boolean            fresh;
  215.         
  216.         if (ref.Error())    {
  217.             File_error(ref.Error());
  218.             
  219.             return nil;
  220.         }
  221.         
  222.         fresh    = (oflag & O_CREAT) && !ref.spec->Exists();
  223.         
  224.         if (MPWDomain::open)
  225.             sock = (Socket *) MPWDomain::open(ref.spec->RelPath(), oflag);
  226.         else
  227.             sock = (Socket *) MacFileSocket::open(*ref.spec, oflag);
  228.         
  229.         if (sock && fresh)
  230.             GUSIConfig.SetDefaultFType(*ref.spec);
  231.     }
  232.     
  233.     return sock;
  234. }
  235.  
  236. #define SFSaveDisk        (* (short *) 0x0214)
  237. #define CurDirStore        (* (long *)  0x0398)
  238.  
  239. static long                     currentDir;
  240. static SFReply                    reply;
  241. static char *                    customPrompt;
  242.  
  243. static int do_getfile(
  244.                     TFileSpec *     defaultFile, 
  245.                     TFileSpec *     result,
  246.                     short                numTypes,
  247.                     SFTypeList        types)
  248. {
  249.     Point        myPoint    =    {75, 75};
  250.     
  251.     if (defaultFile) {
  252.         *defaultFile += (StringPtr) "\p";
  253.         
  254.         SFSaveDisk     = -defaultFile->vRefNum;
  255.         CurDirStore = defaultFile->parID;
  256.     }
  257.     
  258.     SFGetFile(myPoint, (StringPtr) "\p", NULL, numTypes, types, NULL, &reply);
  259.     
  260.     if (reply.good)
  261.         *result = TFileSpec(reply.vRefNum, reply.fName);
  262.     
  263.     return reply.good;
  264. }
  265.  
  266. static pascal Boolean FolderFFilter(ParmBlkPtr p)
  267. {
  268.     return !(p->fileParam.ioFlAttrib & ioDirMask);
  269. }
  270.  
  271. #if GENERATINGCFM
  272. RoutineDescriptor    uFolderFFilter = 
  273.         BUILD_ROUTINE_DESCRIPTOR(uppFileFilterProcInfo, FolderFFilter);
  274. #else
  275. #define uFolderFFilter FolderFFilter
  276. #endif
  277.  
  278. static ControlHandle GetDlgCtrl(DialogPtr dlg, short item)
  279. {
  280.     short     kind;
  281.     Handle    hdl;
  282.     Rect        box;
  283.     
  284.     GetDItem(dlg, item, &kind, &hdl, &box);
  285.     return (ControlHandle) hdl;
  286. }
  287.  
  288. static pascal short GetDirDlgHook(short item, DialogPtr dlgPtr)
  289. {
  290.     switch (item) {
  291.     case sfHookFirstCall:
  292.         if (customPrompt)
  293.             setitext(Handle(GetDlgCtrl(dlgPtr, 13)), customPrompt);
  294.         break;
  295.     case 11:
  296.         if (reply.fType) {
  297.             if (!reply.fName[0])
  298.                 currentDir    =    reply.fType;
  299.             else {
  300.                 TFileSpec dir(reply.vRefNum, CurDirStore, reply.fName);
  301.     
  302.                 dir += (StringPtr) "\p";
  303.                 
  304.                 currentDir = dir.parID;
  305.             }
  306.                 
  307.             return    1;
  308.         }
  309.         break;
  310.     
  311.     case 12:
  312.         currentDir    =    CurDirStore;
  313.         
  314.         return 1;
  315.     case sfHookNullEvent:
  316.         if (!reply.fType)
  317.             HiliteControl(GetDlgCtrl(dlgPtr, 11), 255);
  318.         else
  319.             HiliteControl(GetDlgCtrl(dlgPtr, 11), 0);
  320.         break;
  321.     }
  322.     
  323.     return item;
  324. }
  325.  
  326. #if GENERATINGCFM
  327. RoutineDescriptor    uGetDirDlgHook = 
  328.         BUILD_ROUTINE_DESCRIPTOR(uppDlgHookProcInfo, GetDirDlgHook);
  329. #else
  330. #define uGetDirDlgHook GetDirDlgHook
  331. #endif
  332.  
  333. static int do_getfolder(
  334.                     char *            prompt,
  335.                     TFileSpec *     defaultFile, 
  336.                     TFileSpec *     result)
  337. {
  338.     Point        myPoint    =    {75, 75};
  339.     
  340.     if (defaultFile) {
  341.         *defaultFile += (StringPtr) "\p";
  342.         
  343.         SFSaveDisk     = -defaultFile->vRefNum;
  344.         CurDirStore = defaultFile->parID;
  345.     }
  346.     
  347.     customPrompt = prompt && *prompt ? prompt : nil;
  348.     
  349.     SFPGetFile(
  350.         myPoint, 
  351.         (StringPtr) "\p", 
  352.         FileFilterUPP(&uFolderFFilter), 
  353.         -1, 
  354.         nil, 
  355.         DlgHookUPP(&uGetDirDlgHook), 
  356.         &reply,
  357.         GUSIRsrcID,                
  358.         nil);
  359.  
  360.     if (reply.good) {
  361.         result->vRefNum    =    -SFSaveDisk;
  362.         result->parID        =    currentDir;
  363.         --*result;
  364.     }
  365.     
  366.     return reply.good;
  367. }
  368.  
  369. static int     do_putfile(    
  370.                     char    *            prompt,
  371.                     TFileSpec *     defaultFile, 
  372.                     TFileSpec *     result)
  373. {
  374.     Point            myPoint    =    {75, 75};
  375.     StringPtr    defName;
  376.     Str255        prmpt;
  377.     
  378.     CopyC2PStr(prompt, prmpt);
  379.         
  380.     if (defaultFile) {
  381.         SFSaveDisk     = -defaultFile->vRefNum;
  382.         CurDirStore = defaultFile->parID;
  383.         defName        = defaultFile->name;
  384.     } else
  385.         defName        = (StringPtr) "\p";
  386.     
  387.     SFPutFile(myPoint, prmpt, defName, NULL, &reply);
  388.     
  389.     if (reply.good)
  390.         *result        = TFileSpec(reply.vRefNum, reply.fName);
  391.     
  392.     return reply.good;
  393. }
  394.  
  395. int FileSocketDomain::choose(int, char * prompt, void * constraint, int flags, void * name, int * namelen)
  396. {
  397.     sa_constr_file *     constr = (sa_constr_file *) constraint;
  398.     TFileSpec            file;
  399.     TFileSpec            df;
  400.     TFileSpec *            defaultFile;
  401.     Boolean                good;
  402.     char *                path;
  403.     int                    len;
  404.     
  405.     if (flags & CHOOSE_DEFAULT) {
  406.         df = TFileSpec((char *) name);
  407.         defaultFile = df.Error() ? nil : &df;
  408.     } else
  409.         defaultFile = nil;
  410.         
  411.     if (flags & CHOOSE_NEW)
  412.         good = do_putfile(prompt, defaultFile, &file);
  413.     else if (flags & CHOOSE_DIR)
  414.         good = do_getfolder(prompt, defaultFile, &file);
  415.     else if (constr)
  416.         good = do_getfile(defaultFile, &file, constr->numTypes, constr->types);
  417.     else
  418.         good = do_getfile(defaultFile, &file, -1, nil);
  419.     
  420.     if (good) {
  421.         path    =    file.FullPath();
  422.         len    =    int(strlen(path));
  423.         
  424.         if (len < *namelen)
  425.             memcpy(name, path, (*namelen = len)+1);
  426.         else
  427.             return GUSI_error(EINVAL);
  428.     } else
  429.         return GUSI_error(EINTR);
  430.     
  431.     return 0;
  432. }
  433.  
  434. int FileSocketDomain::remove(const GUSIFileRef & ref)
  435. {
  436.     OSErr err;
  437.     
  438.     if (ref.IsDevice())
  439.         return GUSI_error(EINVAL);
  440.     if (ref.Error())
  441.         return File_error(ref.Error());
  442.         
  443.     switch (err = HDelete(ref.spec->vRefNum, ref.spec->parID, ref.spec->name)) {
  444.     case noErr:
  445.         return 0;
  446.     case fBsyErr: {
  447.         const CInfoPBRec * info = ref.Info();
  448.         
  449.         if (info && !IsFile(*info) && info->dirInfo.ioDrNmFls)
  450.             return GUSI_error(ENOTEMPTY);
  451.         else
  452.             return GUSI_error(EBUSY);
  453.         }
  454.     default:
  455.         return File_error(err);
  456.     }
  457. }
  458.  
  459. int FileSocketDomain::rename(const GUSIFileRef & ref, const char *newname)
  460. {    
  461.     OSErr err;
  462.     
  463.     if (ref.IsDevice())
  464.         return GUSI_error(EINVAL);
  465.     if (ref.Error())
  466.         return File_error(ref.Error());
  467.     if (IsDevice(newname))
  468.         return GUSI_error(EINVAL);
  469.         
  470.     TFileSpec    newnm(newname, true);
  471.     
  472.     switch (err = FSpSmartMove((TFileSpec *)ref.spec, &newnm)) {
  473.     case fBsyErr:
  474.         return GUSI_error(ENOTEMPTY);
  475.     default:
  476.         return File_error(err);
  477.     }
  478.     
  479.     return 0;
  480. }
  481.  
  482. void FileSocketDomain::fsetfileinfo(const GUSIFileRef & ref, unsigned long newcreator, unsigned long newtype)
  483. {
  484.     if (ref.IsDevice()) {
  485.         GUSI_error(EINVAL);
  486.         
  487.         return;
  488.     }
  489.         
  490.     FInfo            info;    
  491.  
  492.     if (ref.Error() || !ref.spec->Exists()
  493.         || HGetFInfo(ref.spec->vRefNum, ref.spec->parID, ref.spec->name, &info)
  494.     ) {
  495.         GUSI_error(EIO);
  496.         
  497.         return;
  498.     }
  499.     
  500.     info.fdType     =    newtype;
  501.     info.fdCreator    =    newcreator;
  502.     
  503.     if (HSetFInfo(ref.spec->vRefNum, ref.spec->parID, ref.spec->name, &info))
  504.         GUSI_error(EIO);
  505.     
  506.     errno = 0;
  507. }
  508.  
  509. void FileSocketDomain::fgetfileinfo(const GUSIFileRef & ref, unsigned long * creator, unsigned long * type)
  510. {
  511.     if (ref.IsDevice()) {
  512.         GUSI_error(EINVAL);
  513.         
  514.         return;
  515.     }
  516.         
  517.     FInfo            info;    
  518.  
  519.     if (ref.Error() || !ref.spec->Exists() 
  520.         || HGetFInfo(ref.spec->vRefNum, ref.spec->parID, ref.spec->name, &info)
  521.     ) {
  522.         GUSI_error(EIO);
  523.         
  524.         return;
  525.     }
  526.     
  527.     if (creator)
  528.         *creator = info.fdCreator;
  529.     
  530.     if (type)
  531.         *type = info.fdType;
  532.     
  533.     errno = 0;
  534. }
  535.  
  536. int FileSocketDomain::faccess(const GUSIFileRef & ref, unsigned int cmd, long* arg)
  537. {
  538.         return GUSI_error(EINVAL);
  539. }
  540.  
  541. static OSErr GetVolume(const CInfoPBRec & cb, ParamBlockRec & pb)
  542. {
  543.     Str63                name;
  544.  
  545.     pb.volumeParam.ioNamePtr    =    name;
  546.     pb.volumeParam.ioVRefNum    =    cb.hFileInfo.ioVRefNum;
  547.     pb.volumeParam.ioVolIndex    =    0;
  548.  
  549.     return PBGetVInfo(&pb, false);
  550. }
  551.  
  552. static int do_stat(const GUSIFileRef & ref, struct stat & buf)
  553. {
  554.     if (!ref.Info())
  555.         return GUSI_error(ENOENT);
  556.         
  557.     const CInfoPBRec &     cb = *ref.Info();
  558.     ParamBlockRec            pb;
  559.     
  560.     if (GetVolume(cb, pb))
  561.         return GUSI_error(ENOENT);
  562.         
  563.     buf.st_dev        =    pb.ioParam.ioVRefNum;
  564.     buf.st_ino        =    cb.dirInfo.ioDrDirID;
  565.     buf.st_nlink    =    1;
  566.     buf.st_uid        =    0;
  567.     buf.st_gid        =    0;
  568.     buf.st_rdev        =    0;
  569.     buf.st_atime    =    cb.hFileInfo.ioFlMdDat;
  570.     buf.st_mtime    =    cb.hFileInfo.ioFlMdDat;
  571.     buf.st_ctime    =    cb.hFileInfo.ioFlCrDat;
  572.     buf.st_blksize    =    pb.volumeParam.ioVAlBlkSiz;
  573.  
  574.     if (!IsFile(cb))    {
  575.         TFileSpec        spec;
  576.         CInfoPBRec        info;
  577.  
  578.         spec.vRefNum    =    pb.ioParam.ioVRefNum;
  579.         spec.parID        =    cb.dirInfo.ioDrDirID;
  580.         
  581.         ++buf.st_nlink;
  582.         
  583.         if (GUSIConfig.accurStat) {
  584.             for (int i = 0; i++ < cb.dirInfo.ioDrNmFls;) {
  585.                 spec    =    spec[i];
  586.                 if (!spec.Error() && !spec.CatInfo(info) && !IsFile(info))
  587.                     ++buf.st_nlink;
  588.             }
  589.         } else {
  590.             buf.st_nlink    +=    cb.dirInfo.ioDrNmFls;
  591.         }
  592.         
  593.         buf.st_mode    =    S_IFDIR | 0666;
  594.         
  595.         if (GUSIExec(ref))
  596.             buf.st_mode    |= 0111;
  597.  
  598.         buf.st_size    =    cb.dirInfo.ioDrNmFls;
  599.     } else if (IsAlias(cb)) {
  600.         buf.st_mode    =    S_IFLNK | 0777;
  601.         buf.st_size    =    cb.hFileInfo.ioFlRLgLen;        /* Data fork is ignored    */
  602.     } else if (cb.hFileInfo.ioFlFndrInfo.fdType == '∑OCK') {
  603.         buf.st_mode    =    S_IFSOCK | 0666;
  604.         buf.st_size    =    cb.hFileInfo.ioFlRLgLen;        /* Data fork is ignored    */
  605.     } else {
  606.         buf.st_mode        =    S_IFREG | 0666;
  607.  
  608.         if (cb.hFileInfo.ioFlAttrib & 0x01)
  609.             buf.st_mode &=    ~0222;
  610.  
  611.         if (GUSIExec(ref))
  612.             buf.st_mode    |= 0111;
  613.  
  614.         buf.st_size    =    cb.hFileInfo.ioFlLgLen;        /* Resource fork is ignored    */
  615.     }
  616.  
  617.     buf.st_blocks    =    (buf.st_size + buf.st_blksize - 1) / buf.st_blksize;
  618.  
  619.     return 0;
  620. }
  621.  
  622. static int do_special_stat(const GUSIFileRef & ref, struct stat & buf)
  623. {
  624.     buf.st_dev            =    0;
  625.     buf.st_ino            =    0;
  626.     buf.st_mode            =    S_IFCHR | 0666 ;
  627.     buf.st_nlink        =    1;
  628.     buf.st_uid            =    0;
  629.     buf.st_gid            =    0;
  630.     buf.st_rdev            =    0;
  631.     buf.st_size            =    1;
  632.     buf.st_atime        =    time(NULL);
  633.     buf.st_mtime        =    time(NULL);
  634.     buf.st_ctime        =    time(NULL);
  635.     buf.st_blksize        =    1;
  636.     buf.st_blocks        =    1;
  637.  
  638.     if (GUSIExec(ref))
  639.         buf.st_mode    |= 0111;
  640.     
  641.     return 0;
  642. }
  643.  
  644. int FileSocketDomain::stat(const GUSIFileRef & ref, struct stat * buf)
  645. {
  646.     if (ref.IsDevice())
  647.         return do_special_stat(ref, *buf);
  648.     else if (ref.Error())
  649.         return GUSI_error(ENOENT);
  650.     else 
  651.         return do_stat(ref, *buf);
  652. }
  653.  
  654. int FileSocketDomain::chmod(const GUSIFileRef & ref, mode_t mode)
  655. {
  656.     const CInfoPBRec * cb;
  657.     
  658.     if (ref.IsDevice())
  659.         return GUSI_error(EINVAL);
  660.         
  661.     if (ref.Error() || !(cb = ref.Info()))
  662.         return GUSI_error(ENOENT);
  663.  
  664.     if (!(cb->dirInfo.ioFlAttrib & 0x10))
  665.         if (mode & S_IWUSR) {
  666.             if (cb->hFileInfo.ioFlAttrib & 0x01)
  667.                 HRstFLock(ref.spec->vRefNum, ref.spec->parID, ref.spec->name);
  668.         } else {
  669.             if (!(cb->hFileInfo.ioFlAttrib & 0x01))
  670.                 HSetFLock(ref.spec->vRefNum, ref.spec->parID, ref.spec->name);
  671.         }
  672.     
  673.     return 0;
  674. }
  675.  
  676. int FileSocketDomain::utime(const GUSIFileRef & ref, const struct utimbuf * times)
  677. {
  678.     if (ref.IsDevice())
  679.         return GUSI_error(EINVAL);
  680.     
  681.     CInfoPBRec                cb;
  682.     const CInfoPBRec *    cbp;
  683.  
  684.     if (ref.Error() || !(cbp = ref.Info()))
  685.         return GUSI_error(ENOENT);
  686.     
  687.     cb                             =    *cbp;
  688.     cb.hFileInfo.ioVRefNum    =    ref.spec->vRefNum;
  689.     cb.hFileInfo.ioDirID        =    ref.spec->parID;
  690.     cb.hFileInfo.ioNamePtr    =    ref.spec->name;
  691.     cb.hFileInfo.ioFlMdDat    =    times ? times->modtime : time(nil);
  692.     // times->actime is ignored. The Mac has no access times.
  693.     
  694.     return File_error(PBSetCatInfoSync(&cb));
  695. }
  696.  
  697. int FileSocketDomain::access(const GUSIFileRef & ref, int mode)
  698. {
  699.     if (ref.IsDevice())
  700.         return GUSI_error(EINVAL);
  701.         
  702.     const CInfoPBRec * info    = ref.Info();
  703.  
  704.     if (!info)
  705.         switch (ref.Error()) {
  706.             case afpAccessDenied:
  707.                 return GUSI_error(EACCES);
  708.             case ioErr:
  709.                 return GUSI_error(EIO);
  710.             case bdNamErr:
  711.             case fnfErr:
  712.             case nsvErr:
  713.             case paramErr:
  714.             case dirNFErr:
  715.             case afpObjectTypeErr:
  716.                 /* ENOENT */
  717.             default:
  718.                 return GUSI_error(ENOENT);        /* Don't know what the error is. */
  719.         }
  720.     
  721.     // 
  722.     // Under our simplifying assumptions, we don't check AppleShare permissions,
  723.     // so if we managed to do a GetCatInfo, R_OK and F_OK are already assumed
  724.     // to be true
  725.     //
  726.     if (mode & W_OK) {
  727.         //
  728.         // Check if volume is locked
  729.         //
  730.         HParamBlockRec    vol;
  731.         
  732.         vol.volumeParam.ioNamePtr    = nil;
  733.         vol.volumeParam.ioVolIndex    = 0;
  734.         vol.volumeParam.ioVRefNum    = ref.spec->vRefNum;
  735.  
  736.         if (PBHGetVInfoSync(&vol))
  737.             return GUSI_error(EINVAL);    // Should never happen
  738.             
  739.         if (vol.volumeParam.ioVAtrb & 0x8080)
  740.             return GUSI_error(EROFS);    // Yup, volume is locked
  741.         
  742.         //
  743.         // Check if file is locked
  744.         //
  745.         if (IsFile(*info) && info->hFileInfo.ioFlAttrib & 0x01)
  746.             return GUSI_error(EACCES); // Yup, file is locked
  747.     }
  748.  
  749.    // 
  750.    // Check executability via Exec hook
  751.    //
  752.    if (mode & X_OK)
  753.        if (!GUSIExec(ref))
  754.            return GUSI_error(EACCES);
  755.            
  756.     return 0;
  757. }
  758.  
  759. Boolean GUSIDefaultExec(const GUSIFileRef & ref)
  760. {
  761.     const CInfoPBRec * info    = ref.Info();
  762.     
  763.     if (info)
  764.         if (IsFile(*info))
  765.             switch (info->hFileInfo.ioFlFndrInfo.fdType) {
  766.             case 'APPL':
  767.             case 'appe':
  768.                 return true;
  769.             }
  770.         else
  771.             return true;    // All directories are considered searchable
  772.     
  773.     return false;
  774. }
  775.  
  776. /************************ FileSocket members ************************/
  777.  
  778. int FileSocket::fcntl(unsigned int, int)
  779. {
  780.     return GUSI_error(EOPNOTSUPP);
  781. }
  782.  
  783. int FileSocket::ioctl(unsigned int, void *)
  784. {
  785.     return GUSI_error(EOPNOTSUPP);
  786. }
  787.  
  788. int FileSocket::fstat(struct stat * buf)
  789. {
  790.     GUSIFileRef    ref(fRefNum, FileSocketDomain::willStat);
  791.  
  792.     if (ref.Error())
  793.         return GUSI_error(ENOENT);
  794.     else
  795.         return do_stat(ref, *buf);
  796. }
  797.  
  798. int FileSocket::select(Boolean * canRead, Boolean * canWrite, Boolean *)
  799. {
  800.     int    goodies    =    0;
  801.     
  802.     // Simplicistic implementation 
  803.     
  804.     if (canRead)    {
  805.         *canRead = true;
  806.         ++goodies;
  807.     }
  808.     
  809.     if (canWrite)    {
  810.         *canWrite = true;
  811.         ++goodies;
  812.     }
  813.     
  814.     return goodies;
  815. }
  816.  
  817. FileSocket::~FileSocket()
  818. {
  819. }
  820.  
  821. /***************** Things that happen to real files only *****************/
  822.  
  823. OSErr VRef2Icon(short vRef, Handle * icon)
  824. {
  825.     OSErr                err;
  826.     HParmBlkPtr        hpp;
  827.     ParmBlkPtr        pp;
  828.     HParamBlockRec    hpb;
  829.     
  830.     hpp                                =    &hpb;
  831.     pp                                    =    (ParmBlkPtr) hpp;
  832.     hpp->volumeParam.ioVRefNum    =    vRef;
  833.     hpp->volumeParam.ioNamePtr    =    nil;
  834.     hpp->volumeParam.ioVolIndex=    0;
  835.     if (err = PBHGetVInfoSync(hpp))
  836.         return err;
  837.         
  838.     pp->cntrlParam.ioVRefNum    =    hpb.volumeParam.ioVDrvInfo;
  839.     pp->cntrlParam.ioCRefNum    =    hpb.volumeParam.ioVDRefNum;
  840.     pp->cntrlParam.csCode        =    21;
  841.     
  842.     if (err = PBControlSync(pp))
  843.         return err;
  844.     
  845.     PtrToHand(*(Ptr *) pp->cntrlParam.csParam, icon, 256);
  846.     
  847.     return noErr;
  848. }
  849.  
  850. typedef OSType    TTypeMap[2];
  851.  
  852. static TTypeMap map[]    =    {
  853.     {'amnu', 'faam'},
  854.     {'ctrl', 'fact'},
  855.     {'extn', 'faex'},
  856.     {'pref', 'fapf'},
  857.     {'prnt', 'fapn'},
  858.     {'empt', 'trsh'},
  859.     {'trsh', 'trsh'},
  860.     {'strt', 'fast'},
  861.     {'macs', 'fasy'},
  862.     {     0,      0}
  863. };
  864.  
  865. #ifdef GUSI_FILE_DEBUG
  866. ostream & operator<<(ostream & str, OSType * ty)
  867. {
  868.     return str     << "'"
  869.                     << char((*ty >> 24) & 0xFF) 
  870.                     << char((*ty >> 16) & 0xFF)
  871.                     << char((*ty >> 8) & 0xFF)
  872.                     << char(*ty & 0xFF)
  873.                     << "'";
  874. }
  875. #endif
  876.  
  877. void OurResidentAliasExpert(
  878.     TFileSpec & file, 
  879.     OSType *     fCreator, 
  880.     OSType *     fType,
  881.     TFileSpec * iconFile,
  882.     short *         iconID)
  883. {
  884.     Boolean                        appleShare;
  885.     CInfoPBRec                    info;
  886.     GetVolParmsInfoBuffer    volParms;
  887.     HParamBlockRec                pb;
  888.     
  889.     *fCreator = 'MACS';
  890.     *iconFile    =    file;
  891.     *iconID        =    kCustomIconResource;
  892.     
  893.     if (file.parID == fsRtParID)
  894.         appleShare    =    true;
  895.     else {
  896.         if (file.CatInfo(info))
  897.             goto error;
  898.         
  899.         appleShare = !IsFile(info);
  900.     }
  901.     
  902.     if (appleShare)    {
  903.         pb.ioParam.ioNamePtr    =    nil;
  904.         pb.ioParam.ioVRefNum    =    file.vRefNum;
  905.         pb.ioParam.ioBuffer    =    Ptr(&volParms);
  906.         pb.ioParam.ioReqCount=    sizeof(GetVolParmsInfoBuffer);
  907.         
  908.         if (PBHGetVolParmsSync(&pb) || !volParms.vMServerAdr)    
  909.             appleShare    =    false;
  910.     }
  911.     
  912.     if (appleShare)
  913.         if (file.parID == fsRtParID)
  914.             *fType    =    'srvr';
  915.         else if (!HasRdPerm(info))
  916.             *fType     =    'fadr';
  917.         else
  918.             *fType    =    'faet';
  919.     else if (file.parID == fsRtParID)
  920.         *fType    =    'hdsk';
  921.     else if (!IsFile(info)) 
  922.         if (DirIsMounted(info))
  923.             *fType = 'famn';
  924.         else if (DirIsExported(info))
  925.             *fType = 'fash';
  926.         else if (DirIsShared(info))
  927.             *fType = 'faet';
  928.         else 
  929.             *fType = 'fdrp';
  930.  
  931.     if (file.parID == fsRtParID)    {
  932.         iconFile->parID    =    fsRtDirID;
  933.         PLstrcpy(iconFile->name, (StringPtr) "\pIcon\n");
  934.     } else if (!IsFile(info))    {
  935.         if (info.dirInfo.ioDrDirID < 9)    {
  936.             short vRef;
  937.             long    dirID;
  938.             
  939.             for (TTypeMap * mapp = map; **mapp; ++mapp)
  940.                 if (!FindFolder(file.vRefNum, (*mapp)[0], false, &vRef, &dirID))
  941.                     if (dirID == info.dirInfo.ioDrDirID) {
  942.                         *fType = (*mapp)[1];
  943.  
  944.                         break;
  945.                     }
  946.         }
  947.         *iconFile += (StringPtr) "\pIcon\n";
  948.     } else {
  949.         *fType    =    info.hFileInfo.ioFlFndrInfo.fdType;
  950.         *fCreator=    info.hFileInfo.ioFlFndrInfo.fdCreator;
  951.     }
  952.     
  953. #ifdef GUSI_FILE_DEBUG
  954.     cerr << "Type = " << fType << ", creator = " << fCreator << endl;
  955.     cerr << "Look for custom icons in " << iconFile->FullPath() << endl;
  956. #endif
  957.  
  958.     return;
  959. error:
  960.     *fType        =    0;
  961.     *fCreator    =    0;
  962. }
  963.  
  964. static OSType iconTypes[]    =    {
  965.     'ICN#',
  966.     'ics#',
  967.     'icl4',
  968.     'ics4',
  969.     'icl8', 
  970.     'ics8',
  971.     0
  972. };
  973.  
  974. Boolean CopyIconFamily(short srcResFile, short srcID, short dstResFile, short dstID)
  975. {
  976.     Handle    icon;
  977.     Boolean    success    =    false;
  978.     OSType * types;
  979.  
  980.     for (types = iconTypes; *types; ++types)    {
  981.         UseResFile(srcResFile);
  982.         if (icon = Get1Resource(*types, srcID))    {
  983.             UseResFile(dstResFile);
  984.             DetachResource(icon);
  985.             AddResource(icon, *types, dstID, (StringPtr) "\p");
  986.         
  987.             success = success || !ResError();
  988.         }
  989.     }
  990.     
  991.     return success;
  992. }
  993.  
  994. Boolean AddIconsToFile(
  995.     const TFileSpec &    origFile,
  996.     short                 aliasFile, 
  997.     OSType                 fCreator, 
  998.     OSType                fType, 
  999.     const FSSpec &        iconFile, 
  1000.     short                    iconID)
  1001. {
  1002.     short        iFile;
  1003.     Boolean    success;
  1004.     Handle     icon;
  1005.  
  1006.     iFile = FSpOpenResFile(&iconFile, fsRdPerm);
  1007.     
  1008.     if (iFile == -1)
  1009.         goto noCustom;
  1010.     
  1011.     success = CopyIconFamily(iFile, iconID, aliasFile, kCustomIconResource);
  1012.     
  1013.     CloseResFile(iFile);
  1014.     
  1015.     if (success)
  1016.         return true;
  1017.  
  1018. #ifdef GUSI_FILE_DEBUG
  1019.     cerr << "No custom Icons found." << endl;
  1020. #endif
  1021.  
  1022. noCustom:    
  1023.     if (fType == 'hdsk' && fCreator == 'MACS')
  1024.         if (!VRef2Icon(origFile.vRefNum, &icon))    {
  1025. #ifdef GUSI_FILE_DEBUG
  1026.             cerr << "Found icon for disk drive." << endl;
  1027. #endif
  1028.             AddResource(icon, 'ICN#', kCustomIconResource, (StringPtr) "\p");
  1029.             
  1030.             return !ResError();
  1031.         }
  1032.                 
  1033.     return false;
  1034. }
  1035.  
  1036. int symlink(const char* linkto, const char* linkname)
  1037. {
  1038.     if (IsDevice(linkto))
  1039.         return GUSI_error(EINVAL);
  1040.     if (IsDevice(linkname))
  1041.         return GUSI_error(EINVAL);
  1042.         
  1043.     OSType        fType;
  1044.     OSType        fCreator;
  1045.     short            iconID;
  1046.     short            aliasFile;
  1047.     AliasHandle    alias;
  1048.     Boolean        customIcon;
  1049.     TFileSpec    iconFile;
  1050.     FInfo            info;
  1051.     
  1052.     if (!hasAlias || !hasMakeFSSpec)
  1053.         return GUSI_error(EOPNOTSUPP);
  1054.         
  1055.     TFileSpec    oldnm(linkto);
  1056.     
  1057.     if (oldnm.Error() || !oldnm.Exists())
  1058.         return GUSI_error(EIO);
  1059.         
  1060.     TFileSpec    newnm(linkname, true);
  1061.  
  1062.     if (newnm.Error())
  1063.         return GUSI_error(EIO);
  1064.     
  1065.     if (newnm.Exists())
  1066.         return GUSI_error(EEXIST);
  1067.     
  1068.     OurResidentAliasExpert(oldnm, &fCreator, &fType, &iconFile, &iconID);
  1069.     
  1070. #ifdef GUSI_FILE_DEBUG
  1071.     cerr << "Creating " << newnm.FullPath() << endl;
  1072. #endif
  1073.  
  1074.     FSpCreateResFile(&newnm, fCreator, fType, smSystemScript);
  1075.     
  1076.     if (ResError())
  1077.         return GUSI_error(EIO);
  1078.     
  1079. #ifdef GUSI_FILE_DEBUG
  1080.     cerr << "Opening " << newnm.FullPath() << endl;
  1081. #endif
  1082.  
  1083.     aliasFile = FSpOpenResFile(&newnm, fsRdWrPerm);
  1084.     
  1085.     if (aliasFile == -1)
  1086.         goto deleteFile;
  1087.     
  1088. #ifdef GUSI_FILE_DEBUG
  1089.     cerr << "Creating alias for " << oldnm.FullPath() << " in " << newnm.FullPath() << endl;
  1090. #endif
  1091.     
  1092.     if (NewAlias(nil, &oldnm, &alias))
  1093.         goto closeFile;
  1094.  
  1095. #ifdef GUSI_FILE_DEBUG
  1096.     cerr << "Adding alias to file." << endl;
  1097. #endif
  1098.     
  1099.     AddResource((Handle) alias, 'alis', 0, oldnm.name);
  1100.     
  1101.     if (ResError())
  1102.         goto deleteAlias;
  1103.     
  1104.     customIcon = AddIconsToFile(oldnm, aliasFile, fCreator, fType, iconFile, iconID);
  1105.  
  1106. #ifdef GUSI_FILE_DEBUG
  1107.     cerr << "There were " << (customIcon ? "" : "no ") << "custom Icons." << endl;
  1108. #endif
  1109.         
  1110.     CloseResFile(aliasFile);
  1111.  
  1112.     FSpGetFInfo(&newnm, &info);
  1113.     info.fdFlags    |=    (1 << 15) | (customIcon ? (1 << 10) : 0);
  1114.     info.fdFlags    &= ~(1 << 8);
  1115.     FSpSetFInfo(&newnm, &info);
  1116.     
  1117.     return 0;
  1118.  
  1119. deleteAlias:
  1120.     DisposHandle((Handle) alias);
  1121. closeFile:
  1122.     CloseResFile(aliasFile);
  1123. deleteFile:
  1124.     FSpDelete(&newnm);    
  1125.     
  1126.     return GUSI_error(EIO);
  1127. }
  1128.  
  1129. int readlink(const char * path, char * buf, int bufsiz)
  1130. {
  1131.     if (IsDevice(path))
  1132.         return GUSI_error(EINVAL);
  1133.     
  1134.     char *         resPath;    
  1135.     int            len;
  1136.     TFileSpec    file(path, true);
  1137.  
  1138.     if (!(resPath = file.FullAliasPath()))
  1139.         if (file.Error() == resFNotFound)
  1140.             return GUSI_error(EINVAL);
  1141.         else
  1142.             return File_error(file.Error());
  1143.         
  1144.     len     = strlen(resPath);
  1145.     strncpy(buf, resPath, bufsiz);
  1146.     
  1147.     if (len >= bufsiz)
  1148.         return GUSI_error(ENAMETOOLONG);
  1149.     else
  1150.         return len;
  1151. }
  1152.  
  1153. #endif
  1154.  
  1155. /************************* directory stuff **************************/
  1156.  
  1157. struct dir {
  1158.     short            index;
  1159.     short            vol;
  1160.     long            dirID;
  1161.     dirent        entry;
  1162. };
  1163.  
  1164. DIR * opendir(const char * name)
  1165. {
  1166.     DIR *            d;
  1167.     TFileSpec    spec(name);
  1168.     
  1169.     if (spec.Error())
  1170.         goto error;
  1171.     
  1172.     spec += (StringPtr) "\p";
  1173.     
  1174.     if (spec.Error())
  1175.         goto error;
  1176.     
  1177.     d = (DIR *) NewPtr(sizeof(DIR));
  1178.     
  1179.     d->index    =    1;
  1180.     d->vol    =    spec.vRefNum;
  1181.     d->dirID    =    spec.parID;
  1182.     
  1183.     return d;
  1184.     
  1185. error:
  1186.     File_error(spec.Error());
  1187.     
  1188.     return nil;
  1189. }
  1190.     
  1191. struct dirent * readdir(DIR * dirp)
  1192. {
  1193.     TFileSpec    spec;
  1194.     int             i; 
  1195.     int            e = errno;
  1196.     
  1197.     spec.vRefNum    =    dirp->vol;
  1198.     spec.parID        =    dirp->dirID;
  1199.     spec.name[0]    =  0;
  1200.     
  1201.     spec = spec[dirp->index++];
  1202.     
  1203.     if (spec.Error())
  1204.         goto error;
  1205.     
  1206.     dirp->entry.d_fileno    =    spec.LastInfo()->dirInfo.ioDrDirID;
  1207.     dirp->entry.d_namlen = *spec.name;
  1208.     
  1209.     memcpy(dirp->entry.d_name, (char *) spec.name+1, dirp->entry.d_namlen);
  1210.     
  1211.     dirp->entry.d_name[dirp->entry.d_namlen] = 0;
  1212.     
  1213.     i = dirp->entry.d_namlen;
  1214.     do 
  1215.         dirp->entry.d_name[i++] = 0;
  1216.     while (i & 3);
  1217.     
  1218.     dirp->entry.d_reclen = sizeof(u_long)+sizeof(u_short)+sizeof(u_short)+i;
  1219.     
  1220.     return &dirp->entry;
  1221.     
  1222. error:
  1223.     File_error(spec.Error());
  1224.     
  1225.     if (errno == ENOENT)
  1226.         errno = e;
  1227.         
  1228.     return nil;
  1229. }
  1230.  
  1231. long telldir(const DIR * dirp)
  1232. {
  1233.     return dirp->index;
  1234. }
  1235.  
  1236. void seekdir(DIR * dirp, long loc)
  1237. {
  1238.     dirp->index    =    (short) loc;
  1239. }
  1240.  
  1241. void rewinddir(DIR * dirp)
  1242. {
  1243.     dirp->index    =    1;
  1244. }
  1245.  
  1246. int closedir(DIR * dirp)
  1247. {
  1248.     DisposPtr((Ptr) dirp);
  1249.     
  1250.     return 0;
  1251. }
  1252.  
  1253. int scandir(
  1254.     const char *         name, 
  1255.     struct dirent *** namelist,
  1256.    int (*want)(struct dirent *), 
  1257.     int (*cmp)(const void *, const void *))
  1258. {
  1259.     struct dirent *    entry;
  1260.     struct dirent *    copy;
  1261.     struct dirent **    names;
  1262.     int                     count;
  1263.     DIR *                    dirp;
  1264.     CInfoPBRec            info;
  1265.     TFileSpec            spec;
  1266.     
  1267.     if ((dirp = opendir(name)) == NULL)
  1268.         return -1;
  1269.  
  1270.     spec.vRefNum    =    dirp->vol;
  1271.     spec.parID        =    dirp->dirID;
  1272.     --spec;
  1273.     
  1274.     if (spec.CatInfo(info))
  1275.         return File_error(spec.Error());
  1276.  
  1277.     names = (struct dirent **) malloc(info.dirInfo.ioDrNmFls * sizeof(struct dirent *));
  1278.     if (names == NULL)
  1279.         return GUSI_error(ENOMEM);
  1280.  
  1281.     count = 0;
  1282.     while ((entry = readdir(dirp)) != NULL) {
  1283.         if (want && !(*want)(entry))
  1284.             continue;    /* Don't want this entry */
  1285.  
  1286.         if (!(copy = (struct dirent *)malloc(entry->d_reclen))) {
  1287.             free(names);
  1288.             
  1289.             return GUSI_error(ENOMEM);
  1290.         }
  1291.         
  1292.         memcpy(copy, entry, entry->d_reclen);
  1293.  
  1294.         names[count++] = copy;
  1295.     }
  1296.     
  1297.     closedir(dirp);
  1298.     
  1299.     if (count && cmp)
  1300.         qsort(names, count, sizeof(struct dirent *), cmp);
  1301.         
  1302.     *namelist = names;
  1303.     
  1304.     return count;
  1305. }
  1306.  
  1307. int chdir(const char * path)    
  1308. {
  1309.     TFileSpec    dir(path);
  1310.     
  1311.     if (dir.Error())
  1312.         return GUSI_error(ENOENT);
  1313.         
  1314.     return File_error(TFileSpec::ChDir(dir));
  1315. }
  1316.  
  1317. int mkdir(const char * path)    
  1318. {
  1319.     OSErr            err;
  1320.     long            nuDir;
  1321.     TFileSpec    dir(path, true);
  1322.     
  1323.     if (dir.Error())
  1324.         return File_error(dir.Error());
  1325.     
  1326.     if (err = DirCreate(dir.vRefNum, dir.parID, dir.name, &nuDir))
  1327.         return File_error(err);
  1328.  
  1329.     return 0;
  1330. }
  1331.  
  1332. int rmdir(const char * path)    
  1333. {
  1334.     OSErr            err;
  1335.     TFileSpec    dir(path);
  1336.     CInfoPBRec  info;
  1337.     
  1338.     if (err = dir.Error())
  1339.         return File_error(err);
  1340.     else if (err = dir.CatInfo(info))
  1341.         return File_error(err);
  1342.     else if (IsFile(info))
  1343.         return GUSI_error(ENOTDIR);
  1344.  
  1345.     if (err = HDelete(dir.vRefNum, dir.parID, dir.name))
  1346.         switch (err)    {
  1347.         default:
  1348.             return File_error(err);
  1349.         case fBsyErr: {
  1350.             if (info.dirInfo.ioDrNmFls)
  1351.                 return GUSI_error(ENOTEMPTY);
  1352.             else
  1353.                 return GUSI_error(EBUSY);
  1354.             }
  1355.         }
  1356.     
  1357.     return 0;
  1358. }
  1359.  
  1360. char * getcwd(char * buf, size_t size)
  1361. {
  1362.     OSErr            err;
  1363.     TFileSpec    cwd;
  1364.     char *         res;
  1365.     
  1366.     if (err = cwd.Default()) {
  1367.         File_error(err);
  1368.     
  1369.         return nil;
  1370.     }
  1371.         
  1372.     res = cwd.FullPath();
  1373.     
  1374.     if (size < strlen(res)+1)
  1375.         return (char *) GUSI_error_nil(size > 0 ? ERANGE : EINVAL);
  1376.     if (!buf && !(buf = (char *) malloc(size)))
  1377.         return (char *) GUSI_error_nil(ENOMEM);
  1378.  
  1379.     strcpy(buf, res);
  1380.     
  1381.     return buf;
  1382. }